 
////////////////////////////////////////////////////////////////////////
// Projekt  : Aufgabe: Objekt Loader			  Datum: 15.05.2012   //
// Autor    : Lukas Sauer                                             //
//                                                                    //
////////////////////////////////////////////////////////////////////////



#include <windows.h>
#include <stdlib.h>
#include <glut.h>
#include <stdio.h>



#include <GL/gl.h>
#include <GL/glu.h>
#include <glext.h>



#include <SDL.h> 
#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
#include <fstream>
#include <cstdio>
#include <iostream>


unsigned int tex;
bool isvertexnormal; 



struct coordinate{
	float x, y, z; 
	coordinate(float a, float b, float c) : x(a), y(b), z(c) {}; 
}; 


struct face{
	int facenum; 
	bool four; 
	int faces[4]; 
	face(int facen, int f1, int f2, int f3) : facenum(facen){
		faces[0]=f1; 
		faces[1]=f2; 
		faces[2]=f3;
		four=false; 		
	}
	face(int facen, int f1, int f2, int f3, int f4): facenum(facen) {
		faces[0]=f1; 
		faces[1]=f2; 
		faces[2]=f3;
		faces[3]=f4;
		four=true; 		
	}
}; 



float angle = 0.0; 

std::vector<std::string*> coord; 
std::vector<coordinate*> vertex; 
std::vector<face*> faces; 
std::vector<coordinate*> normals; 
std::vector<coordinate*> vertexnormals;



unsigned int loadTexture(const char* filename)
{
	SDL_Surface* img=SDL_LoadBMP(filename); 
	unsigned int id; 
	glGenTextures(1, &id); 
	glBindTexture(GL_TEXTURE_2D, id); 
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img->w, img->h, 0, GL_RGB,GL_UNSIGNED_SHORT_5_6_5, img->pixels);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	SDL_FreeSurface(img); 

	return id; 

}

void smoothNormals()
	{
        for(int i=1;i<vertex.size()+1;i++)
        {
                float vecX=0.0,vecY=0.0,vecZ=0.0;
                int num=0;
                for(int j=0;j<faces.size();j++)
                {
                        if(faces[j]->faces[0]==i || faces[j]->faces[1]==i || faces[j]->faces[2]==i || faces[j]->faces[3]==i)
                        {
                                vecX+=normals[faces[j]->facenum-1]->x;
                                vecY+=normals[faces[j]->facenum-1]->y;
                                vecZ+=normals[faces[j]->facenum-1]->z;
                                num++;
                        }
                }
                if(num)
                {
                        vecX/=num;
                        vecY/=num;
                        vecZ/=num;
                }
                float d=sqrt(vecX*vecX+vecY*vecY+vecZ*vecZ);
                if(d)
                {
                        vecX/=d;
                        vecY/=d;
                        vecZ/=d;
                }
                vertexnormals.push_back(new coordinate(vecX,vecY,vecZ));
        }
}





int loadObject(const char* filename)
{
	std::ifstream in(filename);

	if(!in.is_open())
	{
		std::cout << "Not open" << std::endl; 
		return -1; 
	}


	char buf[256]; 
	while(!in.eof())
	{
		in.getline(buf, 256); 
		coord.push_back(new  std::string(buf));
	}
	for(int i = 0; i < coord.size(); i++)
	{
		if((*coord[i])[0]=='#')
			continue; 		
		else if((*coord[i])[0] =='v' && (*coord[i])[1] == ' ' )
		{			
			float tmpx, tmpy, tmpz; 
			sscanf(coord[i]->c_str(), "v %f %f %f",&tmpx, &tmpy, &tmpz); 
			vertex.push_back(new coordinate(tmpx, tmpy, tmpz)); 
			
		}
		else if((*coord[i])[0] =='v' && (*coord[i])[1] =='n' )
		{
			float tmpx, tmpy, tmpz;
			sscanf(coord[i]->c_str(), "vn %f %f %f",&tmpx, &tmpy, &tmpz);
			normals.push_back(new coordinate(tmpx, tmpy, tmpz)); 
			
		}else if((*coord[i])[0]=='f')
		{
			int a, b, c, d, e; 				
		
			if(count(coord[i]->begin(),coord[i]->end(),' ')==3)     //if it is a triangle (it has 3 space in it)
			{
					sscanf(coord[i]->c_str(),"f %d//%d %d//%d %d//%d",&a,&b,&c,&b,&d,&b);
					faces.push_back(new face(b,a,c,d));     //read in, and add to the end of the face list
			}
			else
			{
					sscanf(coord[i]->c_str(),"f %d//%d %d//%d %d//%d %d//%d",&a,&b,&c,&b,&d,&b,&e,&b);
					faces.push_back(new face(b,a,c,d,e));   //do the same, except we call another constructor, and we use different pattern
			}
			
		}

	}



	//draw
	smoothNormals(); 
	int num;        //the id for the list
        num=glGenLists(1);      //generate a uniqe
        glNewList(num,GL_COMPILE);      //and create it
        for(int i=0;i<faces.size();i++)
        {
                if(faces[i]->four)      //if it's a quad draw a quad
                {
                        glBegin(GL_QUADS);                               
                                //glNormal3f(normals[faces[i]->facenum-1]->x,normals[faces[i]->facenum-1]->y,normals[faces[i]->facenum-1]->z);  
								if(isvertexnormal)
								{
									glNormal3f(vertexnormals[faces[i]->faces[0]-1]->x, 
										vertexnormals[faces[i]->faces[0]-1]->y, 
										vertexnormals[faces[i]->faces[0]-1]->z 
										);
								}
								glTexCoord2f(0.0,1.0); 
                                glVertex3f(vertex[faces[i]->faces[0]-1]->x,vertex[faces[i]->faces[0]-1]->y,vertex[faces[i]->faces[0]-1]->z);
								if(isvertexnormal)
								{
									glNormal3f(vertexnormals[faces[i]->faces[1]-1]->x, 
										vertexnormals[faces[i]->faces[1]-1]->y, 
										vertexnormals[faces[i]->faces[1]-1]->z 
										);
								}
								glTexCoord2f(0.0,0.0); 
                                glVertex3f(vertex[faces[i]->faces[1]-1]->x,vertex[faces[i]->faces[1]-1]->y,vertex[faces[i]->faces[1]-1]->z);
								if(isvertexnormal)
								{
									glNormal3f(vertexnormals[faces[i]->faces[2]-1]->x, 
										vertexnormals[faces[i]->faces[2]-1]->y, 
										vertexnormals[faces[i]->faces[2]-1]->z 
										);
								}
								glTexCoord2f(1.0,0.0); 
                                glVertex3f(vertex[faces[i]->faces[2]-1]->x,vertex[faces[i]->faces[2]-1]->y,vertex[faces[i]->faces[2]-1]->z);
								if(isvertexnormal)
								{
									glNormal3f(vertexnormals[faces[i]->faces[3]-1]->x, 
										vertexnormals[faces[i]->faces[3]-1]->y, 
										vertexnormals[faces[i]->faces[3]-1]->z 
										);
								}
								glTexCoord2f(1.0,1.0); 
                                glVertex3f(vertex[faces[i]->faces[3]-1]->x,vertex[faces[i]->faces[3]-1]->y,vertex[faces[i]->faces[3]-1]->z);	



                        glEnd();
                }else{
                        glBegin(GL_TRIANGLES);
                               if(isvertexnormal)
								{
									glNormal3f(vertexnormals[faces[i]->faces[0]-1]->x, 
										vertexnormals[faces[i]->faces[0]-1]->y, 
										vertexnormals[faces[i]->faces[0]-1]->z 
										);
								}
								glTexCoord2f(0.0,1.0); 
                                glVertex3f(vertex[faces[i]->faces[0]-1]->x,vertex[faces[i]->faces[0]-1]->y,vertex[faces[i]->faces[0]-1]->z);
								if(isvertexnormal)
								{
									glNormal3f(vertexnormals[faces[i]->faces[1]-1]->x, 
										vertexnormals[faces[i]->faces[1]-1]->y, 
										vertexnormals[faces[i]->faces[1]-1]->z 
										);
								}
								glTexCoord2f(0.0,0.0); 
                                glVertex3f(vertex[faces[i]->faces[1]-1]->x,vertex[faces[i]->faces[1]-1]->y,vertex[faces[i]->faces[1]-1]->z);
								if(isvertexnormal)
								{
									glNormal3f(vertexnormals[faces[i]->faces[2]-1]->x, 
										vertexnormals[faces[i]->faces[2]-1]->y, 
										vertexnormals[faces[i]->faces[2]-1]->z 
										);
								}

                        glEnd();
                }
        }

	glEndList(); 

	for(int i=0; i<coord.size();i++)	
		delete coord[i];
	
	for(int i=0; i<faces.size(); i++)
		delete faces[i];

	for(int i=0; i<normals.size();i++)	
		delete normals[i];
	
	for(int i=0; i<vertex.size(); i++)
		delete vertex[i];
    for(int i=0;i<vertexnormals.size();i++)
            delete vertexnormals[i];;

	return num; 
}

int obj1;


void init()
{
		isvertexnormal = true; 

        glClearColor(0.0,0.0,0.0,1.0);
		glOrtho(-1.0, 1.0, -1.0, 1.0, -3.0, 3.0); 
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();



        //gluPerspective(0.0,0.0,0.0,0.0);
		gluPerspective(45,640.0/600.0,1.0,500.0);
        glMatrixMode(GL_MODELVIEW);   
		glEnable(GL_TEXTURE_2D); 

		char *cmds[] = {"monkey", "cube", "link"};
		char tmp[12];

		int i=0; 

		do{

			puts ("Welches Objekt soll geladen werden?");
			puts ("\nMoegliche Optionen: \n");
			puts ("monkey		//Gouraud-Shading");
			puts ("cube		//Texture");
			puts ("link		//Gouraud-Shading\n");
			
			gets (tmp);

			if(strcmp(cmds[0], tmp) ==0)
			{
				puts ("Objekt wird geladen! Bitte warten...\n(Wartezeit fast 30 Seks)");				
				obj1=loadObject("sphere.obj");	
				i=1; 
			}
			if(strcmp(cmds[1], tmp) ==0)
			{
				puts ("Objekt wird geladen! Bitte warten...");
				tex = loadTexture("schach.bmp"); 
				obj1=loadObject("link.obj");	
				i=1; 
			}
			if(strcmp(cmds[2], tmp) ==0)
			{
				puts ("Objekt wird geladen! Bitte warten...\n(Wartezeit fast 1 Minute)");
				obj1=loadObject("link_zelda.obj");
				i=1; 
			}
			else{

				puts ("Die Eingabe war ungueltig, versuchen Sie es erneut");
			}

		
		



		}while(i!=1);

	
		


		
		//Lightning set up 
		glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); 
        glEnable(GL_LIGHTING);  //we enable lighting, to make the 3D object to 3D
        glEnable(GL_LIGHT0);
		

		//Set lightning intensity and color
		GLfloat qaAmbientLight[] = {0.2, 0.2, 0.2, 1.0}; 
		GLfloat qaDiffuseLight[] = {0.8, 0.8, 0.8, 1.0}; 
		GLfloat qaSpecularLight[] = {1.0, 1.0, 1.0, 1.0}; 
		glLightfv(GL_LIGHT0, GL_AMBIENT, qaAmbientLight);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, qaDiffuseLight);
		glLightfv(GL_LIGHT0, GL_SPECULAR, qaSpecularLight);




		/*
        float col[]={0.0,1.0,0.0,1.0};  //light color is white
        glLightfv(GL_LIGHT0,GL_DIFFUSE,col);
		*/


		//Set the light position
		GLfloat qaLightPosition[] = {0.0, 1.0,-1.5, 1.0}; 
		glLightfv(GL_LIGHT0, GL_POSITION, qaLightPosition); 

}

void display()
{
		//Set material properties

		GLfloat qaEmission[] = {0.0, 0.0, 0.0, 0.0};		
		GLfloat qaBlack[] = {0.0, 0.0, 0.0, 1.0}; 
		GLfloat qaGreen[] = {0.0, 1.0, 0.0, 1.0}; 
		GLfloat qaBlue[] = {0.0, 0.0, 1.0, 1.0}; 
		GLfloat qaRed[] = {1.0, 0.0, 0.0, 1.0}; 
		GLfloat qaWhite[] = {1.0, 1.0, 1.0, 1.0}; 
		GLfloat qaLowAmbient[] = {0.2, 0.2, 0.2, 1.0}; 
		GLfloat qaFullAmbient[] = {1.0, 1.0, 1.0, 1.0}; 

		//Set ambient, diffuse and specular lightning
		glMaterialfv(GL_FRONT, GL_EMISSION, qaEmission);
		glMaterialfv(GL_FRONT, GL_AMBIENT, qaWhite); 
		glMaterialfv(GL_FRONT, GL_DIFFUSE, qaWhite); 
		glMaterialfv(GL_FRONT, GL_SPECULAR, qaWhite); 
		glMaterialf(GL_FRONT, GL_SHININESS, 128.0); 
		glLightfv(GL_LIGHT0, GL_AMBIENT, qaLowAmbient); 



		



        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
		
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_NORMALIZE); 
		glEnable(GL_RESCALE_NORMAL);
		glEnable(GL_SMOOTH); 
        glLoadIdentity();

       // float pos[]={-1.0,1.0,-2.0,1.0};        //set the position
        //glLightfv(GL_LIGHT0,GL_POSITION,pos);		
		
        //glTranslatef(0.0,-3.0,-5.0);
		glTranslatef(0.0,-0.0,-5.0);
        glRotatef(angle,0.0,1.0,0.0);
		glRotatef(angle,1.0,0.0,0.0);
		gluLookAt (0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);// Reset The Current Modelview Matrix


		glBindTexture(GL_TEXTURE, tex); 
       



		//glBindTexture(GL_TEXTURE, tex);
		
		glCallList(obj1);       //draw the 3D mesh		//glTexCoord2f(0.0,0.0);		
/*
		glBegin(GL_QUADS); 
			glTexCoord2f(0.0,1.0); 
			glVertex3f(-2.0,2.0,0.0); 
			glTexCoord2f(0.0,0.0); 
			glVertex3f(-2.0,-2.0,0.0); 
			glTexCoord2f(1.0,0.0); 
			glVertex3f(2.0,-2.0,0.0); 
			glTexCoord2f(1.0,1.0); 
			glVertex3f(2.0,2.0,0.0); 
		glEnd(); 

		*/
		
		



		glutSwapBuffers();
		glFlush();		
}




void reshape (int w, int h)
{
   
   glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
   glMatrixMode (GL_MODELVIEW);
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 27:
         exit(0);
         break;
   }
}

void idle( void )
{
	angle+= 0.02f;
	glutPostRedisplay();
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);  
   glutInitWindowSize (750, 750); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();   
   glutDisplayFunc(display); 
   glutIdleFunc( idle );
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0;
}
